/**
 * 排班计算工具函数
 */

/**
 * 生成本地日期字符串，避免时区问题
 * @param date 日期对象
 * @returns YYYY-MM-DD 格式的日期字符串
 */
function getLocalDateString(date: Date): string {
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, '0');
  const day = String(date.getDate()).padStart(2, '0');
  return `${year}-${month}-${day}`;
}

export interface ScheduleItem {
  date: string;
  shiftType: string;
  dayOfMonth: number;
  weekday: string;
}

export interface ScheduleOptions {
  shiftType: '三班倒' | '两班倒' | '上一休二';
  workDate: string;
  shiftTag?: string; // 班次标签，用于标识班次组或自定义班次名称
  shiftSequence?: string[]; // 班次序列，如 ["A", "B", "休"] 或 ["班", "休"]，如果不提供则使用默认值
  year?: number;
  month?: number;
}

// 班次类型配置
export interface ShiftTypeConfig {
  name: string;
  displayName: string;
  cssClass: string;
  color: string;
  description?: string;
}

// 排班模式配置
export interface ShiftPatternConfig {
  name: string;
  displayName: string;
  shiftTypes: ShiftTypeConfig[];
  cycle: number; // 循环天数
}

// 预定义的班次类型
export const SHIFT_TYPES: Record<string, ShiftTypeConfig> = {
  '白班': {
    name: '白班',
    displayName: '白班',
    cssClass: 'day-shift',
    color: '#0ea5e9',
    description: '白班工作时段'
  },
  '夜班': {
    name: '夜班',
    displayName: '夜班',
    cssClass: 'night-shift',
    color: '#64748b',
    description: '夜班工作时段'
  },
  '班': {
    name: '班',
    displayName: '班',
    cssClass: 'shift',
    color: '#10b981', // 更新为绿色系，更现代
    description: '普通班次'
  },
  '休': {
    name: '休',
    displayName: '休',
    cssClass: 'rest-day',
    color: '#8b5cf6', // 更新为紫色系，更现代
    description: '休息日'
  },
  '休1': {
    name: '休1',
    displayName: '休1',
    cssClass: 'rest-day',
    color: '#8b5cf6',
    description: '休息日1'
  },
  '休2': {
    name: '休2',
    displayName: '休2',
    cssClass: 'rest-day',
    color: '#8b5cf6',
    description: '休息日2'
  }
};

// 预定义的排班模式
export const SHIFT_PATTERNS: Record<string, ShiftPatternConfig> = {
  '三班倒': {
    name: '三班倒',
    displayName: '三班倒',
    shiftTypes: [SHIFT_TYPES['白班'], SHIFT_TYPES['夜班'], SHIFT_TYPES['休']],
    cycle: 3
  },
  '两班倒': {
    name: '两班倒',
    displayName: '两班倒',
    shiftTypes: [SHIFT_TYPES['班'], SHIFT_TYPES['休']],
    cycle: 2
  },
  '上一休二': {
    name: '上一休二',
    displayName: '上一休二',
    shiftTypes: [SHIFT_TYPES['班'], SHIFT_TYPES['休1'], SHIFT_TYPES['休2']],
    cycle: 3
  }
};

/**
 * 获取班次类型配置
 * @param shiftTypeName 班次类型名称
 * @returns 班次类型配置
 */
export function getShiftTypeConfig(shiftTypeName: string): ShiftTypeConfig | null {
  return SHIFT_TYPES[shiftTypeName] || null;
}

/**
 * 获取排班模式配置
 * @param patternName 排班模式名称
 * @returns 排班模式配置
 */
export function getShiftPatternConfig(patternName: string): ShiftPatternConfig | null {
  return SHIFT_PATTERNS[patternName] || null;
}

/**
 * 获取所有可用的排班模式
 * @returns 排班模式列表
 */
export function getAvailableShiftPatterns(): ShiftPatternConfig[] {
  return Object.values(SHIFT_PATTERNS);
}

/**
 * 获取所有可用的班次类型
 * @returns 班次类型列表
 */
export function getAvailableShiftTypes(): ShiftTypeConfig[] {
  return Object.values(SHIFT_TYPES);
}

/**
 * 计算指定月份的排班
 * @param options 排班选项
 * @returns 排班数据
 */
export function calculateMonthlySchedule(options: ScheduleOptions): Record<string, string> {
  const { shiftType, workDate, shiftTag, year, month } = options;

  if (!shiftType || !workDate) {
    return {};
  }

  const workDateObj = new Date(workDate);

  // 如果没有指定年月，则使用工作日期所在的年月
  let targetYear = year;
  let targetMonth = month;

  if (targetYear === undefined || targetMonth === undefined) {
    targetYear = workDateObj.getFullYear();
    targetMonth = workDateObj.getMonth();
  }

  // 获取指定月份的总天数
  const monthDays = new Date(targetYear, targetMonth + 1, 0).getDate();
  const scheduleData: Record<string, string> = {};

  // 三班倒：使用动态班次序列，如 ["A", "B", "休"] 或 ["白班", "夜班", "休"]
  if (shiftType === '三班倒') {
    // 获取班次序列，优先使用传入的序列，否则使用默认值
    const shiftSequence = options.shiftSequence || ['白班', '夜班', '休'];
    const cycle = shiftSequence.length;

    // 如果指定了班次标签，以该标签为起始点重新计算排班
    if (shiftTag) {
      // 根据班次标签在序列中找到对应的索引
      let startShiftIndex = shiftSequence.indexOf(shiftTag);
      // 如果找不到，尝试兼容旧的标签名称
      if (startShiftIndex === -1) {
        if (shiftTag === '白班' || shiftTag === '早班' || shiftTag === '日班') {
          startShiftIndex = shiftSequence.findIndex(s => s === '白班' || s === 'A' || s === '早班' || s === '日班');
          if (startShiftIndex === -1) startShiftIndex = 0; // 默认第一个
        } else if (shiftTag === '夜班' || shiftTag === '晚班' || shiftTag === '大夜班') {
          startShiftIndex = shiftSequence.findIndex(s => s === '夜班' || s === 'B' || s === '晚班' || s === '大夜班');
          if (startShiftIndex === -1) startShiftIndex = 1; // 默认第二个
        } else if (shiftTag === '休' || shiftTag === '休息' || shiftTag === '休假') {
          startShiftIndex = shiftSequence.findIndex(s => s === '休' || s === '休息' || s === '休假');
          if (startShiftIndex === -1) startShiftIndex = cycle - 1; // 默认最后一个
        } else {
          // 如果完全找不到，尝试直接匹配
          startShiftIndex = shiftSequence.indexOf(shiftTag);
          if (startShiftIndex === -1) startShiftIndex = 0; // 默认第一个
        }
      }

      const workDay = workDateObj.getDate(); // 获取工作日期的天数

      // 直接遍历当月的每一天，从1号开始
      for (let day = 1; day <= monthDays; day++) {
        const currentDate: Date = new Date(targetYear, targetMonth, day);

        // 计算从工作日期开始的天数差
        const dayDiff = day - workDay;
        // 以工作日期为基准，加上起始班次索引，然后取模
        const mod = (dayDiff + startShiftIndex) % cycle;
        // 确保模运算结果为非负数
        const finalMod = mod < 0 ? mod + cycle : mod;

        const shiftTypeName = shiftSequence[finalMod];
        const dateKey = getLocalDateString(currentDate);
        scheduleData[dateKey] = shiftTypeName;
      }
    } else {
      // 原有的排班逻辑（以工作日期为起始点）
      // 直接遍历当月的每一天，从1号开始
      for (let day = 1; day <= monthDays; day++) {
        const currentDate: Date = new Date(targetYear, targetMonth, day);
        const dayCount = Math.abs(Math.floor((currentDate.getTime() - workDateObj.getTime()) / (1000 * 60 * 60 * 24)));
        const mod = dayCount % cycle;

        const shiftTypeName = shiftSequence[mod] || shiftSequence[0];
        const dateKey = getLocalDateString(currentDate);
        scheduleData[dateKey] = shiftTypeName;
      }
    }
  } else if (shiftType === '两班倒') {
    // 获取班次序列，优先使用传入的序列，否则使用默认值
    const shiftSequence = options.shiftSequence || ['班', '休'];
    const cycle = shiftSequence.length;

    // 如果指定了班次标签，以该标签为起始点重新计算排班
    if (shiftTag) {
      // 根据班次标签在序列中找到对应的索引
      let startShiftIndex = shiftSequence.indexOf(shiftTag);
      // 如果找不到，尝试兼容旧的标签名称
      if (startShiftIndex === -1) {
        if (shiftTag === '班' || shiftTag === '工作班' || shiftTag === '生产班') {
          startShiftIndex = 0; // 默认第一个
        } else if (shiftTag === '休' || shiftTag === '休息' || shiftTag === '休假') {
          startShiftIndex = cycle - 1; // 默认最后一个
        } else {
          startShiftIndex = 0; // 默认第一个
        }
      }

      const workDay = workDateObj.getDate(); // 获取工作日期的天数

      // 直接遍历当月的每一天，从1号开始
      for (let day = 1; day <= monthDays; day++) {
        const currentDate: Date = new Date(targetYear, targetMonth, day);

        // 计算从工作日期开始的天数差
        const dayDiff = day - workDay;
        // 以工作日期为基准，加上起始班次索引，然后取模
        const mod = (dayDiff + startShiftIndex) % cycle;
        // 确保模运算结果为非负数
        const finalMod = mod < 0 ? mod + cycle : mod;

        const shiftTypeName = shiftSequence[finalMod];
        const dateKey = getLocalDateString(currentDate);
        scheduleData[dateKey] = shiftTypeName;
      }
    } else {
      // 原有的排班逻辑（以工作日期为起始点）
      // 直接遍历当月的每一天，从1号开始
      for (let day = 1; day <= monthDays; day++) {
        const currentDate: Date = new Date(targetYear, targetMonth, day);
        const dayCount = Math.abs(Math.floor((currentDate.getTime() - workDateObj.getTime()) / (1000 * 60 * 60 * 24)));
        const mod = dayCount % cycle;

        const shiftTypeName = shiftSequence[mod] || shiftSequence[0];
        const dateKey = getLocalDateString(currentDate);
        scheduleData[dateKey] = shiftTypeName;
      }
    }
  } else if (shiftType === '上一休二') {
    // 获取班次序列，优先使用传入的序列，否则使用默认值
    const shiftSequence = options.shiftSequence || ['班', '休1', '休2'];
    const cycle = shiftSequence.length;

    // 如果指定了班次标签，以该标签为起始点重新计算排班
    if (shiftTag) {
      // 根据班次标签在序列中找到对应的索引
      let startShiftIndex = shiftSequence.indexOf(shiftTag);
      // 如果找不到，尝试兼容旧的标签名称
      if (startShiftIndex === -1) {
        if (shiftTag === '班' || shiftTag === '工作班' || shiftTag === '生产班') {
          startShiftIndex = 0; // 默认第一个
        } else if (shiftTag === '休1' || shiftTag === '休息1') {
          startShiftIndex = shiftSequence.findIndex(s => s === '休1' || s === '休息1');
          if (startShiftIndex === -1) startShiftIndex = 1; // 默认第二个
        } else if (shiftTag === '休2' || shiftTag === '休息2') {
          startShiftIndex = shiftSequence.findIndex(s => s === '休2' || s === '休息2');
          if (startShiftIndex === -1) startShiftIndex = 2; // 默认第三个
        } else {
          startShiftIndex = 0; // 默认第一个
        }
      }

      const workDay = workDateObj.getDate(); // 获取工作日期的天数

      // 直接遍历当月的每一天，从1号开始
      for (let day = 1; day <= monthDays; day++) {
        const currentDate: Date = new Date(targetYear, targetMonth, day);

        // 计算从工作日期开始的天数差
        const dayDiff = day - workDay;
        // 以工作日期为基准，加上起始班次索引，然后取模
        const mod = (dayDiff + startShiftIndex) % cycle;
        // 确保模运算结果为非负数
        const finalMod = mod < 0 ? mod + cycle : mod;

        const shiftTypeName = shiftSequence[finalMod];
        const dateKey = getLocalDateString(currentDate);
        scheduleData[dateKey] = shiftTypeName;
      }
    } else {
      // 原有的排班逻辑（以工作日期为起始点）
      // 直接遍历当月的每一天，从1号开始
      for (let day = 1; day <= monthDays; day++) {
        const currentDate: Date = new Date(targetYear, targetMonth, day);
        const dayCount = Math.abs(Math.floor((currentDate.getTime() - workDateObj.getTime()) / (1000 * 60 * 60 * 24)));
        const mod = dayCount % cycle;

        const shiftTypeName = shiftSequence[mod] || shiftSequence[0];
        const dateKey = getLocalDateString(currentDate);
        scheduleData[dateKey] = shiftTypeName;
      }
    }
  }

  return scheduleData;
}

/**
 * 计算指定日期范围的排班
 * @param options 排班选项
 * @param startDate 开始日期
 * @param endDate 结束日期
 * @returns 排班数据
 */
export function calculateDateRangeSchedule(
  options: ScheduleOptions,
  startDate: string,
  endDate: string
): Record<string, string> {
  const { shiftType, workDate, shiftTag } = options;

  if (!shiftType || !workDate) {
    return {};
  }

  const workDateObj = new Date(workDate);
  const startDateObj = new Date(startDate);
  const endDateObj = new Date(endDate);

  if (startDateObj > endDateObj) {
    return {};
  }

  const scheduleData: Record<string, string> = {};
  const oneDay = 24 * 60 * 60 * 1000;

  // 获取班次序列，优先使用传入的序列，否则使用默认值
  let shiftSequence: string[] = [];
  let cycle = 0;

  if (options.shiftType === '三班倒') {
    shiftSequence = options.shiftSequence || ['白班', '夜班', '休'];
    cycle = shiftSequence.length;
  } else if (options.shiftType === '两班倒') {
    shiftSequence = options.shiftSequence || ['班', '休'];
    cycle = shiftSequence.length;
  } else if (options.shiftType === '上一休二') {
    shiftSequence = options.shiftSequence || ['班', '休1', '休2'];
    cycle = shiftSequence.length;
  }

  // 如果指定了班次标签，以该标签为起始点重新计算排班
  if (shiftTag && shiftSequence.length > 0) {
    // 根据班次标签在序列中找到对应的索引
    let startShiftIndex = shiftSequence.indexOf(shiftTag);
    // 如果找不到，尝试兼容旧的标签名称
    if (startShiftIndex === -1) {
      if (options.shiftType === '三班倒') {
        if (shiftTag === '白班' || shiftTag === '早班' || shiftTag === '日班') {
          startShiftIndex = shiftSequence.findIndex(s => s === '白班' || s === 'A' || s === '早班' || s === '日班');
          if (startShiftIndex === -1) startShiftIndex = 0;
        } else if (shiftTag === '夜班' || shiftTag === '晚班' || shiftTag === '大夜班') {
          startShiftIndex = shiftSequence.findIndex(s => s === '夜班' || s === 'B' || s === '晚班' || s === '大夜班');
          if (startShiftIndex === -1) startShiftIndex = 1;
        } else if (shiftTag === '休' || shiftTag === '休息' || shiftTag === '休假') {
          startShiftIndex = shiftSequence.findIndex(s => s === '休' || s === '休息' || s === '休假');
          if (startShiftIndex === -1) startShiftIndex = cycle - 1;
        } else {
          startShiftIndex = 0;
        }
      } else if (options.shiftType === '两班倒') {
        if (shiftTag === '班' || shiftTag === '工作班' || shiftTag === '生产班') {
          startShiftIndex = 0;
        } else if (shiftTag === '休' || shiftTag === '休息' || shiftTag === '休假') {
          startShiftIndex = cycle - 1;
        } else {
          startShiftIndex = 0;
        }
      } else if (options.shiftType === '上一休二') {
        if (shiftTag === '班' || shiftTag === '工作班' || shiftTag === '生产班') {
          startShiftIndex = 0;
        } else if (shiftTag === '休1' || shiftTag === '休息1') {
          startShiftIndex = shiftSequence.findIndex(s => s === '休1' || s === '休息1');
          if (startShiftIndex === -1) startShiftIndex = 1;
        } else if (shiftTag === '休2' || shiftTag === '休息2') {
          startShiftIndex = shiftSequence.findIndex(s => s === '休2' || s === '休息2');
          if (startShiftIndex === -1) startShiftIndex = 2;
        } else {
          startShiftIndex = 0;
        }
      }
    }

    for (let currentDate = new Date(startDateObj); currentDate <= endDateObj; currentDate.setTime(currentDate.getTime() + oneDay)) {
      // 计算从工作日期开始的天数差
      const dayCount = Math.floor((currentDate.getTime() - workDateObj.getTime()) / oneDay);
      // 以工作日期为基准，加上起始班次索引，然后取模
      const mod = (dayCount + startShiftIndex) % cycle;
      // 确保模运算结果为非负数
      const finalMod = mod < 0 ? mod + cycle : mod;

      const shiftTypeName = shiftSequence[finalMod];
      const dateKey = getLocalDateString(currentDate);
      scheduleData[dateKey] = shiftTypeName;
    }
  } else {
    // 原有的排班逻辑（以工作日期为起始点）
    for (let currentDate = new Date(startDateObj); currentDate <= endDateObj; currentDate.setTime(currentDate.getTime() + oneDay)) {
      const dayCount = Math.abs(Math.floor((currentDate.getTime() - workDateObj.getTime()) / oneDay));

      let shiftTypeName = '';
      if (shiftSequence.length > 0) {
        const mod = dayCount % cycle;
        shiftTypeName = shiftSequence[mod] || shiftSequence[0];
      } else {
        // 如果没有提供序列，使用默认值（向后兼容）
        if (options.shiftType === '三班倒') {
          const mod = dayCount % 3;
          if (mod === 0) {
            shiftTypeName = '白班';
          } else if (mod === 1) {
            shiftTypeName = '夜班';
          } else if (mod === 2) {
            shiftTypeName = '休';
          }
        } else if (options.shiftType === '两班倒') {
          const mod = dayCount % 2;
          if (mod === 0) {
            shiftTypeName = '班';
          } else if (mod === 1) {
            shiftTypeName = '休';
          }
        } else if (options.shiftType === '上一休二') {
          const mod = dayCount % 3;
          if (mod === 0) {
            shiftTypeName = '班';
          } else if (mod === 1) {
            shiftTypeName = '休1';
          } else if (mod === 2) {
            shiftTypeName = '休2';
          }
        }
      }

      const dateKey = getLocalDateString(currentDate);
      scheduleData[dateKey] = shiftTypeName;
    }
  }

  return scheduleData;
}

/**
 * 获取排班项的样式类名
 * @param shiftType 班次类型
 * @returns 样式类名
 */
export function getScheduleItemClass(shiftType: string): string {
  const baseClass = 'schedule-item';

  // 支持动态班次名称，通过班次名称的前缀或关键词来判断样式
  if (shiftType.includes('白班') || shiftType.includes('白') || shiftType.includes('日班')) {
    return `${baseClass} day-shift`;
  } else if (shiftType.includes('夜班') || shiftType.includes('夜') || shiftType.includes('晚班')) {
    return `${baseClass} night-shift`;
  } else if (shiftType.includes('班') && !shiftType.includes('白班') && !shiftType.includes('夜班')) {
    return `${baseClass} shift`;
  } else if (shiftType.includes('休') || shiftType.includes('休息')) {
    return `${baseClass} rest-day`;
  } else {
    return baseClass;
  }
}

/**
 * 格式化日期显示
 * @param dateStr 日期字符串
 * @returns 格式化后的日期
 */
export function formatScheduleDate(dateStr: string): string {
  const date = new Date(dateStr);
  const month = date.getMonth() + 1;
  const day = date.getDate();
  return `${month}/${day}`;
}

/**
 * 获取指定日期的星期
 * @param dateStr 日期字符串
 * @returns 星期几
 */
export function getWeekday(dateStr: string): string {
  const date = new Date(dateStr);
  const weekdays = ['日', '一', '二', '三', '四', '五', '六'];
  return `周${weekdays[date.getDay()]}`;
}

/**
 * 应用班次标签映射到排班数据
 * @param scheduleData 原始排班数据
 * @param shiftTag 班次标签
 * @returns 应用标签后的排班数据
 */
export function applyShiftTagMapping(
  scheduleData: Record<string, string>,
  shiftTag: string
): Record<string, string> {
  if (!shiftTag) {
    return scheduleData;
  }

  const mappedData: Record<string, string> = {};

  // 班次标签只是用来标识当前班次组的名称，不应该改变原有的排班规律
  // 我们只需要将班次名称替换为标签名称，但保持原有的排班逻辑
  // 注意：此函数主要用于向后兼容，新的实现已经在 calculateMonthlySchedule 中处理了动态序列
  for (const [date, shift] of Object.entries(scheduleData)) {
    // 支持动态班次名称（如 A、B 等）
    if (shift === '白班' || shift === 'A') {
      // 白班/A 保持白班的性质，只是名称可能改变
      if (shiftTag === '白班' || shiftTag === '早班' || shiftTag === '日班' || shiftTag === 'A') {
        mappedData[date] = shiftTag; // 使用标签名称
      } else {
        mappedData[date] = shift; // 保持原值
      }
    } else if (shift === '夜班' || shift === 'B') {
      // 夜班/B 保持夜班的性质，只是名称可能改变
      if (shiftTag === '夜班' || shiftTag === '晚班' || shiftTag === '大夜班' || shiftTag === 'B') {
        mappedData[date] = shiftTag; // 使用标签名称
      } else {
        mappedData[date] = shift; // 保持原值
      }
    } else if (shift === '班') {
      // 普通班次根据标签映射
      if (shiftTag === '班' || shiftTag === '工作班' || shiftTag === '生产班') {
        mappedData[date] = shiftTag;
      } else {
        mappedData[date] = '班';
      }
    } else if (shift === '休') {
      // 休息日保持不变
      mappedData[date] = '休';
    } else if (shift === '休1') {
      // 休息日1保持不变
      mappedData[date] = '休1';
    } else if (shift === '休2') {
      // 休息日2保持不变
      mappedData[date] = '休2';
    } else {
      // 其他情况保持原值（包括 A、B 等动态班次）
      mappedData[date] = shift;
    }
  }

  return mappedData;
}

/**
 * 验证排班类型是否有效
 * @param shiftType 排班类型
 * @returns 是否有效
 */
export function isValidShiftType(shiftType: string): boolean {
  return ['三班倒', '两班倒', '上一休二'].includes(shiftType);
}

/**
 * 验证日期格式是否有效
 * @param dateStr 日期字符串
 * @returns 是否有效
 */
export function isValidDate(dateStr: string): boolean {
  const date = new Date(dateStr);
  return !isNaN(date.getTime());
}

/**
 * 获取排班统计信息
 * @param scheduleData 排班数据
 * @returns 统计信息
 */
export function getScheduleStats(scheduleData: Record<string, string>) {
  const stats = {
    total: 0,
    dayShift: 0,
    nightShift: 0,
    workShift: 0,
    restShift: 0
  };

  Object.values(scheduleData).forEach(shiftType => {
    stats.total++;
    switch (shiftType) {
      case '白班':
        stats.dayShift++;
        break;
      case '夜班':
        stats.nightShift++;
        break;
      case '班':
        stats.workShift++;
        break;
      case '休':
        stats.restShift++;
        break;
    }
  });

  return stats;
}
